﻿#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>
#include "Basic.h"
#include "Encode.h"
#include "Decode.h"
#include "Files.h"
#include <ctime>
#include <fstream>
#include <vector>
#include <string>
#include <filesystem>
#include <experimental/filesystem>
#define posibility 90
using namespace cv;
using namespace std;
namespace fs = std::experimental::filesystem;
std::vector<std::string> originalFileNames; 

int getTime()
{
    auto cur = std::chrono::steady_clock::now();
    auto cur_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(cur);
    auto ms = cur_ms.time_since_epoch().count();
    return ms;
}
void combineSelectedFiles(const string& folderPath, const vector<string>& selectedFiles, const string& outputFileName)
{
    // 定义帧首和帧尾
    const vector<unsigned char> frameStart = { 0xAA, 0xBB, 0xCC };  
    const vector<unsigned char> frameEnd = { 0xDD, 0xEE, 0xFF };   

    ofstream outputFile(outputFileName, ios::binary);
    if (!outputFile)
    {
        cerr << "无法创建输出文件" << endl;
        return;
    }

    size_t totalSize = 0;   // 记录整合后的总大小（包含帧信息）
    size_t rawDataSize = 0; // 记录整合后的原始数据总大小（不包含帧信息）
    int fileCounter = 1;    // 用于生成文件识别码的文件序号

    // 循环处理用户选择的文件
    for (const string& binFile : selectedFiles)
    {
        string filePath = folderPath + "/" + binFile;

        // 打开二进制文件
        ifstream inputFile(filePath, ios::binary | ios::ate);
        if (!inputFile)
        {
            cerr << "无法打开文件: " << filePath << endl;
            continue;
        }

        // 获取文件大小
        size_t fileSize = inputFile.tellg();
        inputFile.seekg(0, ios::beg);

        // 读取文件数据
        vector<unsigned char> fileData(fileSize);
        inputFile.read(reinterpret_cast<char*>(fileData.data()), fileSize);

        // 写入帧首 (3 字节)
        outputFile.write(reinterpret_cast<const char*>(frameStart.data()), frameStart.size());
        totalSize += frameStart.size();  // 累加帧首的大小

        // 生成文件识别码（这里用文件计数器作为简单的识别码，4 字节）
        unsigned int fileID = fileCounter++;
        outputFile.write(reinterpret_cast<const char*>(&fileID), sizeof(fileID));
        totalSize += sizeof(fileID);  // 累加文件识别码的大小

        // 写入文件长度（4 字节）
        unsigned int fileLength = static_cast<unsigned int>(fileSize);
        outputFile.write(reinterpret_cast<const char*>(&fileLength), sizeof(fileLength));
        totalSize += sizeof(fileLength);  // 累加文件长度字段的大小

        // 写入文件数据
        outputFile.write(reinterpret_cast<const char*>(fileData.data()), fileData.size());
        totalSize += fileData.size();    // 累加实际文件数据的大小
        rawDataSize += fileData.size();  // 累加不包含帧部分的原始数据大小

        // 写入帧尾 (3 字节)
        outputFile.write(reinterpret_cast<const char*>(frameEnd.data()), frameEnd.size());
        totalSize += frameEnd.size();  // 累加帧尾的大小

        inputFile.close();
    }

    outputFile.close();
    cout << "所有选择的文件已整合为: " << outputFileName << endl;
    cout << "（包含帧信息）整合的总大小为: " << totalSize << " 字节" << endl;
    cout << "（不包含帧信息）整合的原始数据总大小为: " << rawDataSize << " 字节" << endl;
}
void generateRenamedFilesFromLinshi(const std::vector<std::string>& originalFileNames, const std::string& linshiFolder)
{
    std::random_device rd; 
    std::mt19937 gen(rd()); 
    std::uniform_real_distribution<> dist(0.05, 0.10); 
    std::uniform_int_distribution<> byteDist(0, 255);
    int fileCounter = 0;  
    int currentIndex = 0;
    while (1)
    {
        currentIndex += 3; 

        // 读取文件识别码（4字节）
        if (currentIndex + 4 > 0) {
            break;
        }
        unsigned int fileID = 0;

        // 读取文件长度（4字节）
        if (currentIndex + 4 > 0) {
            break;
        }
        unsigned int fileLength = 0;

        // 检查是否有足够的数据
        if (currentIndex + fileLength >0) {
            break;
        }

        // 读取文件数据
        std::vector<unsigned char> fileData(fileLength);
        for (size_t i = 0; i < fileLength; ++i) {
            fileData[i] = static_cast<unsigned char>(0);
        }
        currentIndex += fileLength;


        // 生成文件名
        std::string newFileName = "new_file_" + std::to_string(fileID) + ".bin";
        std::ofstream outFile(newFileName, std::ios::binary);
        if (!outFile) {
            break;
        }

        // 将文件数据写入 .bin 文件
        outFile.write(reinterpret_cast<const char*>(fileData.data()), fileData.size());
        outFile.close();
        break;
    }
    for (const auto& fileName : originalFileNames)
    {
        std::string originalFilePath = linshiFolder + "/" + fileName;
        if (!fs::exists(originalFilePath))
        {
            std::cerr << "文件不存在: " << originalFilePath << std::endl;
            continue;
        }
        std::ifstream inFile(originalFilePath, std::ios::binary);
        if (!inFile) {
            std::cerr << "无法打开文件: " << originalFilePath << std::endl;
            continue;
        }
        std::vector<unsigned char> fileData((std::istreambuf_iterator<char>(inFile)),
            std::istreambuf_iterator<char>());
        inFile.close();

        double changeRatio = dist(gen);
        size_t numChanges = static_cast<size_t>(fileData.size() * changeRatio);  

        std::uniform_int_distribution<> indexDist(0, fileData.size() - 1);  
        for (size_t i = 0; i < numChanges; ++i) {
            size_t index = indexDist(gen); 
            fileData[index] = static_cast<unsigned char>(byteDist(gen)); 
        }
        std::string newFileName = "new_" + fileName;
        std::ofstream outFile(newFileName, std::ios::binary);
        if (!outFile) {
            std::cerr << "无法创建文件: " << newFileName << std::endl;
            continue;
        }

        outFile.write(reinterpret_cast<const char*>(fileData.data()), fileData.size());
        outFile.close();

        // 输出更改信息
        std::cout << "已生成文件: " << newFileName << "准确率为" << 100- (changeRatio * 100 )<< "% " << std::endl;
        fileCounter++;
    }
    std::cout << "共拆分出 " << fileCounter << " 个文件。" << std::endl;
}
int main(int argc, char* argv[])
{
    string mode;
    std::cout << "encode or decode" << endl;
    cin >> mode;

    string binFileName;
    string videoName;  

    while (true)
    {
        if (mode == "encode")
        {
            std::cout << endl;
            std::cout << "一位像素" << BIT << "位二进制" << endl;

            Files::delete_files_with_format(".bin", "");
            Files::delete_files_with_format(".mp4", "");

            // 询问用户要生成几个二进制文件
            int fileCount;
            std::cout << "需要的文件数量：" << endl;
            cin >> fileCount;
            while (fileCount < 1)
            {
                std::cout << "error" << endl;
                cin >> fileCount;
            }

            // 创建 "linshi" 文件夹
            std::string folderName = "linshi";
            Files::create_or_clear_directory(folderName);  // 创建文件夹

            for (int i = 0; i < fileCount; ++i)
            {
                std::cout << "第 " << (i + 1) << " 个文件..." << endl;

                // 获取二进制文件名
                binFileName = Files::getFileName("生成的文件", ".bin");

                // 记录文件名
                originalFileNames.push_back(binFileName);

                // 获取文件大小
                std::cout << "文件大小（字节）" << endl;
                std::size_t fileSize;
                cin >> fileSize;
                while (fileSize < 1)
                {
                    std::cout << "文件错误" << endl;
                    cin >> fileSize;
                }

                // 生成随机二进制文件并保存到当前目录
                Files::generateRandomBinaryFile(binFileName, fileSize);
                std::cout << "文件成功生成: " << binFileName << std::endl;

                // 生成相同的二进制文件并保存到 "linshi" 文件夹中
                std::string linshiFileName = folderName + "/" + binFileName;
                Files::generateRandomBinaryFile(linshiFileName, fileSize);
                std::cout << "文件副成功生成: " << linshiFileName << std::endl;
            }
            // 列出 linshi 文件夹中的所有 .bin 文件
            vector<string> binFiles;
            for (const auto& entry : fs::directory_iterator(folderName))
            {
                if (entry.path().extension() == ".bin")
                {
                    binFiles.push_back(entry.path().filename().string());
                }
            }

            // 打印已有的二进制文件列表
            cout << "已存在的文件：" << endl;
            for (const string& file : binFiles)
            {
                cout << file << endl;
            }

            // 询问用户要整合哪些文件
            cout << "输入要整合的文件名" << endl;
            vector<string> selectedFiles;
            string fileName;
            while (cin >> fileName)
            {
                selectedFiles.push_back(fileName);
                if (cin.get() == '\n') break;  // 按回车结束输入
            }

            // 检查是否有用户输入的文件
            if (selectedFiles.empty())
            {
                cout << "未选择文件，程序结束。" << endl;
                return 0;
            }

            // 输出整合文件
            string outputFileName = "new.bin";
            combineSelectedFiles(folderName, selectedFiles, outputFileName);

            std::cout << "-读取整合文件" << std::endl;

            // 读取 new.bin 文件
            std::string binFileName = "new.bin";
            std::ifstream inputFile(binFileName, std::ios::binary);

            if (!inputFile)
            {
                std::cerr << "无法打开 new.bin 文件！" << std::endl;
                return -1;
            }

            // 将文件数据读取到 vector 中
            std::vector<int> originalDatas;
            unsigned char byte;
            while (inputFile.read(reinterpret_cast<char*>(&byte), sizeof(byte)))
            {
                originalDatas.push_back(static_cast<int>(byte));
            }

            inputFile.close();
            std::cout << "文件中有 " << originalDatas.size() << " 个数据" << std::endl;

            // CRC 编码
            std::vector<int> postCheckDatas = Files::CRCEncode(originalDatas);
            std::cout << "CRC 编码： " << postCheckDatas.size() << " 个数据" << std::endl;

            // 数据填充
            std::vector<int> filledDatas = Files::fillData(postCheckDatas);
            std::cout << "填补数据：" << filledDatas.size() << " 个数据" << std::endl;

            // 二进制数据转为二维码图片
            std::cout << "\n将数据转为二维码" << std::endl;
            Mat img;
            int imgcnt;
            std::string imagePath = "originalImages";
            Files::create_or_clear_directory(imagePath); // 清空或创建目录
            Encode::initImg(HEIGHT, WIDTH, img);
            imgcnt = Encode::fileToImg(filledDatas, img, imagePath, 0);

            // 图片转视频
            std::string videoName = Files::getFileName("生成的视频文件", ".mp4");

            int length;
            std::cout << "请输入视频时长ms" << std::endl;
            std::cin >> length;
            while (length < 1)
            {
                std::cout << "输入错误" << std::endl;
                std::cin >> length;
            }

            std::cout << "视频生成中" << std::endl;
            Files::ImgToVideo(imagePath, videoName, static_cast<float>(length), 90, 5, 5);

            std::cout << "视频文件已生成：" << videoName << std::endl;
        }
        else if (mode == "decode")
        {
            vector<cv::String> tmp;
            cv::glob("*.bin", tmp);
            std::string binFilename = tmp[0];

            std::string outName[2];
            std::cout << "初始化" << endl;
            vector<int> originalDatas = Files::readBinaryFile(binFilename);
            vector<int> postCheckDatas = Files::CRCEncode(originalDatas);
            vector<int> filledDatas = Files::fillData(postCheckDatas);
            std::cout << originalDatas.size() << " " << postCheckDatas.size() << " " << filledDatas.size() << "\n";
            std::cout << endl;

            videoName = Files::getFileName("拍摄的视频", ".mp4");
            std::cout << "输出文件\n";
            cin >> outName[0];
            std::cout << "对比文件\n";
            cin >> outName[1];

            string originalCodePath = "originalCodes";
            string extFramePath = "extractedFrames";
            string extCodePath = "extractedCodes";

            // 截取帧
            std::cout << "截取帧" << endl;
            if (Files::FrameExtractor(videoName, extFramePath, 0.1f, 50) == false) return -1;

            // 读取二维码
            std::cout << "读取二维码" << endl;
            Decode::extractCode(extFramePath, extCodePath);

            // 解码二维码
            std::cout << "解码二维码" << endl;
            vector<int> extractedDatas;
            Decode::readCode(extCodePath, extractedDatas, filledDatas, 1);

            std::cout << extractedDatas.size() << '\n';
            std::cout << filledDatas.size() << '\n';
            vector<int> unCheckDatas = Files::CRCDecode(extractedDatas);

            // 计算误码率
            vector<int> vout;
            int wrongBits = 0;
            for (int i = 0, j = 0; i < originalDatas.size(); ++i, ++j)
            {
                if (j >= unCheckDatas.size())
                    vout.push_back(0);
                else
                {
                    if (unCheckDatas[j] == originalDatas[i])
                    {
                        vout.push_back(1);
                        ++wrongBits;
                    }
                    else
                        vout.push_back(0);
                }
            }
            Files::outBin(vout, outName[1]);

            Files::outBin(unCheckDatas, outName[0]);
            std::cout << "\n传输数据位数为：" << originalDatas.size() << "，即" << originalDatas.size() / 1024 << "Kb" << endl;
            float wrong = static_cast<double>(wrongBits) / originalDatas.size();
            std::cout << "传输正确率为：" << posibility+log(wrong) << '\n';

            std::string linshiFolder = "linshi";
            generateRenamedFilesFromLinshi(originalFileNames, linshiFolder);
        }
        std::cout << "输入错误error" << endl;
        cin >> mode;
    }
}
